// https://github.com/mrdoob/three.js/blob/dev/examples/js/postprocessing/OutlinePass.js
const MaskVertex = `
void main() {
    gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}`;

const MaskFragment = `
void main() {
    gl_FragColor = vec4(0.0, 1.0, 1.0, 1.0);
}`;

const EdgeVertex = `
varying vec2 vUv;
void main() {
    vUv = uv;
    gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}
`;

const EdgeFragment = `
uniform sampler2D maskTexture;
uniform sampler2D maskTextureHover;
uniform sampler2D maskTextureThird;
uniform vec2 texSize;
uniform vec3 visibleEdgeColor;
uniform vec3 hoverColor;
uniform vec3 thirdColor;
uniform float thickness;

varying vec2 vUv;

void main() {
    vec2 invSize = thickness / texSize;
    vec4 uvOffset = vec4(1.0, 0.0, 0.0, 1.0) * vec4(invSize, invSize);

    vec4 c1 = texture2D( maskTexture, vUv + uvOffset.xy);
    vec4 c2 = texture2D( maskTexture, vUv - uvOffset.xy);
    vec4 c3 = texture2D( maskTexture, vUv + uvOffset.yw);
    vec4 c4 = texture2D( maskTexture, vUv - uvOffset.yw);

    float diff1 = (c1.r - c2.r)*0.5;
    float diff2 = (c3.r - c4.r)*0.5;
    float d = length(vec2(diff1, diff2)) * 1.5;

    // gl_FragColor = d > 0.0 ? vec4(visibleEdgeColor, 1.0) : vec4(0.0, 0.0, 0.0, 0.0);
    vec4 b1 = texture2D( maskTextureHover, vUv + uvOffset.xy);
    vec4 b2 = texture2D( maskTextureHover, vUv - uvOffset.xy);
    vec4 b3 = texture2D( maskTextureHover, vUv + uvOffset.yw);
    vec4 b4 = texture2D( maskTextureHover, vUv - uvOffset.yw);
    diff1 = (b1.r - b2.r)*0.5;
    diff2 = (b3.r - b4.r)*0.5;
    float d2 = length( vec2(diff1, diff2) ) * 1.5;
    
    vec4 e1 = texture2D( maskTextureThird, vUv + uvOffset.xy);
    vec4 e2 = texture2D( maskTextureThird, vUv - uvOffset.xy);
    vec4 e3 = texture2D( maskTextureThird, vUv + uvOffset.yw);
    vec4 e4 = texture2D( maskTextureThird, vUv - uvOffset.yw);
    diff1 = (e1.r - e2.r)*0.5;
    diff2 = (e3.r - e4.r)*0.5;
    float d3 = length( vec2(diff1, diff2) ) * 1.5;

    gl_FragColor = vec4(visibleEdgeColor, 1.0) * vec4(d) + 
      vec4(hoverColor, 1.0) * vec4(d2) + 
      vec4(thirdColor, 1.0) * vec4(d3);
}`;

// shaders for glow
const PrepareMaskVertex = `
#include <morphtarget_pars_vertex>
#include <skinning_pars_vertex>

varying vec4 projTexCoord;
varying vec4 vPosition;
uniform mat4 textureMatrix;

void main() {

  #include <skinbase_vertex>
  #include <begin_vertex>
  #include <morphtarget_vertex>
  #include <skinning_vertex>
  #include <project_vertex>

  vPosition = mvPosition;
  vec4 worldPosition = modelMatrix * vec4( transformed, 1.0 );
  projTexCoord = textureMatrix * worldPosition;

}
`;

const PrepareMaskFragment = `
#include <packing>
varying vec4 vPosition;
varying vec4 projTexCoord;
uniform sampler2D depthTexture;
uniform vec2 cameraNearFar;

void main() {

  float depth = unpackRGBAToDepth(texture2DProj( depthTexture, projTexCoord ));
  float viewZ = - DEPTH_TO_VIEW_Z( depth, cameraNearFar.x, cameraNearFar.y );
  float depthTest = (-vPosition.z > viewZ) ? 1.0 : 0.0;
  gl_FragColor = vec4(0.0, depthTest, 1.0, 1.0);

}
`;

const SeperableBlurVertex = `
varying vec2 vUv;

void main() {
  vUv = uv;
  gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}
`;

const SeperableBlurFragment = `
#include <common>
varying vec2 vUv;
uniform sampler2D colorTexture;
uniform vec2 texSize;
uniform vec2 direction;
uniform float kernelRadius;

float gaussianPdf(in float x, in float sigma) {
  return 0.39894 * exp( -0.5 * x * x/( sigma * sigma))/sigma;
}

void main() {
  vec2 invSize = 1.0 / texSize;
  float weightSum = gaussianPdf(0.0, kernelRadius);
  vec4 diffuseSum = texture2D( colorTexture, vUv) * weightSum;
  vec2 delta = direction * invSize * kernelRadius/float(MAX_RADIUS);
  vec2 uvOffset = delta;
  for( int i = 1; i <= MAX_RADIUS; i ++ ) {
    float w = gaussianPdf(uvOffset.x, kernelRadius);
    vec4 sample1 = texture2D( colorTexture, vUv + uvOffset);
    vec4 sample2 = texture2D( colorTexture, vUv - uvOffset);
    diffuseSum += ((sample1 + sample2) * w);
    weightSum += (2.0 * w);
    uvOffset += delta;
  }
  gl_FragColor = diffuseSum/weightSum;
}
`;

const EdgeFragmentV2 = `
varying vec2 vUv;

uniform sampler2D maskTexture;
uniform vec2 texSize;
uniform vec3 visibleEdgeColor;
uniform vec3 hiddenEdgeColor;

void main() {
  vec2 invSize = 1.0 / texSize;
  vec4 uvOffset = vec4(1.0, 0.0, 0.0, 1.0) * vec4(invSize, invSize);
  vec4 c1 = texture2D( maskTexture, vUv + uvOffset.xy);
  vec4 c2 = texture2D( maskTexture, vUv - uvOffset.xy);
  vec4 c3 = texture2D( maskTexture, vUv + uvOffset.yw);
  vec4 c4 = texture2D( maskTexture, vUv - uvOffset.yw);
  float diff1 = (c1.r - c2.r)*0.5;
  float diff2 = (c3.r - c4.r)*0.5;
  float d = length( vec2(diff1, diff2) );
  float a1 = min(c1.g, c2.g);
  float a2 = min(c3.g, c4.g);
  float visibilityFactor = min(a1, a2);
  vec3 edgeColor = 1.0 - visibilityFactor > 0.001 ? visibleEdgeColor : hiddenEdgeColor;
  gl_FragColor = vec4(edgeColor, 1.0) * vec4(d);
}
`;

const OverlayFragment = `
varying vec2 vUv;

uniform sampler2D maskTexture;
uniform sampler2D edgeTexture1;
uniform sampler2D edgeTexture2;
uniform float edgeStrength;
uniform float edgeGlow;

void main() {
  vec4 edgeValue1 = texture2D(edgeTexture1, vUv);
  vec4 edgeValue2 = texture2D(edgeTexture2, vUv);
  vec4 maskColor = texture2D(maskTexture, vUv);
  float visibilityFactor = 1.0 - maskColor.g > 0.0 ? 1.0 : 0.5;
  vec4 edgeValue = edgeValue1 + edgeValue2 * edgeGlow;
  vec4 finalColor = edgeStrength * maskColor.r * edgeValue;
  gl_FragColor = finalColor;
}
`;

export { MaskVertex, MaskFragment, EdgeVertex, EdgeFragment };
export {
  PrepareMaskVertex,
  PrepareMaskFragment,
  SeperableBlurVertex,
  SeperableBlurFragment,
  EdgeFragmentV2,
  OverlayFragment,
};

export const BloomOverlayFragment = `
uniform sampler2D baseTexture;
uniform sampler2D bloomTexture;
varying vec2 vUv;

void main() {
  gl_FragColor = ( texture2D( baseTexture, vUv ) + vec4( 1.0 ) * texture2D( bloomTexture, vUv ) );
}
`;

export const SeperableBlurUnrealFragment = `
#include <common>
varying vec2 vUv;
uniform sampler2D colorTexture;
uniform vec2 texSize;
uniform vec2 direction;

float gaussianPdf(in float x, in float sigma) {
  return 0.39894 * exp( -0.5 * x * x/( sigma * sigma))/sigma;
}
void main() {
  vec2 invSize = 1.0 / texSize;
  float fSigma = float(SIGMA);
  float weightSum = gaussianPdf(0.0, fSigma);
  vec3 diffuseSum = texture2D( colorTexture, vUv).rgb * weightSum;
  for( int i = 1; i < KERNEL_RADIUS; i ++ ) {
    float x = float(i);
    float w = gaussianPdf(x, fSigma);
    vec2 uvOffset = direction * invSize * x;
    vec3 sample1 = texture2D( colorTexture, vUv + uvOffset).rgb;
    vec3 sample2 = texture2D( colorTexture, vUv - uvOffset).rgb;
    diffuseSum += (sample1 + sample2) * w;
    weightSum += 2.0 * w;
  }
  gl_FragColor = vec4(diffuseSum/weightSum, 1.0);
}
`;

export const BloomCompositeMaterial = `
varying vec2 vUv;
uniform sampler2D blurTexture1;
uniform sampler2D blurTexture2;
uniform sampler2D blurTexture3;
uniform sampler2D blurTexture4;
uniform sampler2D blurTexture5;
uniform sampler2D dirtTexture;
uniform float bloomStrength;
uniform float bloomRadius;
uniform float bloomFactors[NUM_MIPS];
uniform vec3 bloomTintColors[NUM_MIPS];

float lerpBloomFactor(const in float factor) {
  float mirrorFactor = 1.2 - factor;
  return mix(factor, mirrorFactor, bloomRadius);
}

void main() {
  vec4 fragColor = bloomStrength * ( 
    lerpBloomFactor(bloomFactors[0]) * vec4(bloomTintColors[0], 1.0) * texture2D(blurTexture1, vUv) +
    lerpBloomFactor(bloomFactors[1]) * vec4(bloomTintColors[1], 1.0) * texture2D(blurTexture2, vUv) +
    lerpBloomFactor(bloomFactors[2]) * vec4(bloomTintColors[2], 1.0) * texture2D(blurTexture3, vUv) +
    lerpBloomFactor(bloomFactors[3]) * vec4(bloomTintColors[3], 1.0) * texture2D(blurTexture4, vUv) +
    lerpBloomFactor(bloomFactors[4]) * vec4(bloomTintColors[4], 1.0) * texture2D(blurTexture5, vUv) );
  // float val = (fragColor.r + fragColor.g + fragColor.b) * 0.4;
  vec3 luma = vec3( 0.299, 0.587, 0.114 );
  float val = dot(fragColor.rgb, luma);
  fragColor.a = val;
  gl_FragColor = fragColor;
}
`;
